package ac.github.oa.api

import ac.github.oa.OriginAttribute
import ac.github.oa.api.common.Matcher
import ac.github.oa.api.event.entity.EntityDamageEvent
import ac.github.oa.api.event.entity.EntityLoadEquipmentEvent
import ac.github.oa.api.event.entity.ProxyDamageEvent
import ac.github.oa.api.event.item.ItemCreatedEvent
import ac.github.oa.api.event.item.ItemDurabilityDamageEvent
import ac.github.oa.api.event.item.ItemUpdateEvent
import ac.github.oa.internal.core.attribute.AttributeManager
import ac.github.oa.internal.core.attribute.equip.Hand
import ac.github.oa.internal.core.attribute.equip.OffHand
import ac.github.oa.internal.core.item.ItemInstance
import ac.github.oa.internal.core.item.ItemPlant
import ac.github.oa.internal.core.item.generator.SourceGenerator
import org.bukkit.Material
import org.bukkit.entity.LivingEntity
import org.bukkit.entity.Player
import org.bukkit.event.Event
import org.bukkit.inventory.Inventory
import org.bukkit.inventory.ItemStack
import taboolib.common.platform.event.EventPriority
import taboolib.common.platform.event.SubscribeEvent
import taboolib.common5.Coerce
import taboolib.module.nms.ItemTagData
import taboolib.module.nms.getItemTag
import taboolib.platform.util.isAir
import taboolib.platform.util.modifyLore
import kotlin.math.max
import kotlin.math.min

object ItemAPI {

    private val durabilityChar: Char
        get() = OriginAttribute.config.getString("options.durability-char", "/")!!.toCharArray()[0]

    private val durabilityKeyword: String
        get() = OriginAttribute.config.getString("options.durability")!!

    private val damageRemove: Boolean
        get() = OriginAttribute.config.getBoolean("options.damage-remove")

    private val isReadNBT: Boolean
        get() = OriginAttribute.config.getBoolean("options.read-nbt")

    /**
     * 保留旧耐久
     */
//    @SubscribeEvent
//    fun e(e: ItemUpdateEvent) {
//        val newItemStack = e.newItemStack
//        val oldItemStack = e.oldItemStack
//
//        // 如果武器存在耐久节点
//        if (e.item.config.contains("nbt.${NBT.MAX_DURABILITY.key}")) {
//            val max = e.item.config.getInt("nbt.${NBT.MAX_DURABILITY.key}")
//            val durability = getDurability(oldItemStack)
//            setDurability(newItemStack, durability)
//            ItemDurabilityDamageEvent(e.player, newItemStack, max, max, durability).call()
//        }
//    }

    /**
     * 渲染装备战斗力
     */
    @SubscribeEvent
    fun e(e: ItemCreatedEvent) {
        if (e.generator is SourceGenerator) {
            val itemStack = e.itemStack
            val attributeData = OriginAttributeAPI.loadList(itemStack.itemMeta?.lore ?: emptyList())
            attributeData.autoCombatPower()
            val result = Coerce.toInteger(attributeData.combatPower).toString()
            itemStack.modifyLore {
                this.forEachIndexed { index, s ->
                    this[index] = s.replace("%combat-power%", result)
                }
            }
            itemStack.updateDurability()
        }
    }

//    @SubscribeEvent
//    fun e0(e: ItemDurabilityDamageEvent) {
//        val itemStack = e.itemStack
//        val durability = getDurability(itemStack)
//        if (durability <= 0) {
//            ItemPlant.parseItem(itemStack) ?: return
//            itemStack.amount = 0
//        }
//    }

    @SubscribeEvent(ignoreCancelled = true, priority = EventPriority.MONITOR)
    fun e(e: EntityDamageEvent) {
        if (e.isCancelled || e.isPre) return
        // 扣除攻击者的主手副手耐久
        (e.attacker as? Player)?.let { player ->
            AttributeManager.get(player).getItems(true) { slot is Hand || slot is OffHand }.forEach {
                it.item.takeDurability(player, 1, e)
            }
        }
        // 扣除被攻击者的身体物品耐久
        (e.victim as? Player)?.let { player ->
            e.damageMemory.victimData.getItems(true) { true }.forEach {
                // 减少主手盾的防御
                if (it.slot !is Hand) {
                    it.item.takeDurability(player, 1, e)
                }
            }
        }
    }


    @SubscribeEvent(ignoreCancelled = true, priority = EventPriority.LOW)
    fun e(e: ItemDurabilityDamageEvent.Post) {
        val itemStack = e.itemStack
        val item = ItemPlant.parseItem(itemStack) ?: return
        if (item.config.contains("nbt.${NBT.MAX_DURABILITY.key}")) {
            itemStack.modifyLore {
                this.forEachIndexed { index, s ->
                    if (s.contains(durabilityKeyword) && s.contains(durabilityChar)) {
                        val matcher = Matcher.range(s, durabilityChar.toString())
                        this[index] = s.replaceFirst(matcher.pos1.toInt().toString(), e.value.toString())
                            .replace(matcher.pos2.toInt().toString(), e.max.toString())
                    }
                }

            }

        }
    }

    @SubscribeEvent(ignoreCancelled = true)
    fun e(e: EntityLoadEquipmentEvent.Render) {
        if (isReadNBT) {
            val items = e.attributeData.items
            val listOf = mutableListOf<String>()
            // 从NBT内获取属性
            items.filter { it.enable }.forEach {
                it.item.getItemTag()["attributes"]?.asList()?.forEach {
                    listOf += it.asString()
                }
            }
            // 载入属性
            e.attributeData.merge(OriginAttributeAPI.loadList(e.entity, listOf))
        }

    }

    fun checkUpdate(player: Player, inventory: Inventory) {
        (0 until inventory.size).forEach { i ->
            val item = inventory.getItem(i)
            if (item.isAir()) {
                return@forEach
            }
            val canUpdate = item!!.canUpdate()
            if (canUpdate) {
                val itemInstance = ItemInstance.get(item)!!
                val event = ItemUpdateEvent(player, item, itemInstance.rebuild(player), itemInstance.item)
                event.call()
                inventory.setItem(i, event.newItemStack)
            }
        }
    }

    fun ItemStack.canUpdate(): Boolean {
        val item = ItemPlant.parseItem(this) ?: return false
        if (!item.isUpdate) return false
        val itemTag = this.getItemTag()
        val asLong = itemTag["oa-hasCode"]?.asInt() ?: 0
        return asLong != item.hasCode
    }


    fun ItemStack.takeDurability(player: Player? = null, value: Int = 1, source: Any? = null) {
        if (this.type == Material.AIR) return
        val itemTag = getItemTag()
        if (itemTag.containsKey(NBT.MAX_DURABILITY.key)) {
            val max = itemTag[NBT.MAX_DURABILITY.key]!!.asInt()
            val old = itemTag[NBT.DURABILITY.key]?.asInt() ?: max
            val event = ItemDurabilityDamageEvent.Pre(player, this, max, old, value, source)
            if (event.call()) {
                val durability = (old - event.value).coerceAtLeast(0).coerceAtMost(max)
                if (durability == 0 && damageRemove) {
                    this.amount = 0
                } else {
                    itemTag[NBT.DURABILITY.key] = ItemTagData(durability)
                    itemTag.saveTo(this)
                    this.updateDurability(max, durability)
                    ItemDurabilityDamageEvent.Post(player, this, max, old, durability, source).call()
                }
            }
        }
    }

    fun getMaxDurability(itemStack: ItemStack): Int {
        val itemTag = itemStack.getItemTag()
        return itemTag[NBT.MAX_DURABILITY.key]?.asInt() ?: -1;
    }

    fun getDurability(itemStack: ItemStack): Int {
        val itemTag = itemStack.getItemTag()
        if (itemTag.containsKey(NBT.MAX_DURABILITY.key)) {
            val maxDurability = itemTag[NBT.MAX_DURABILITY.key]!!.asInt()
            return (itemTag[NBT.DURABILITY.key]?.asInt() ?: maxDurability)
        }
        return -1;
    }

    fun setDurability(itemStack: ItemStack, value: Int) {
        val itemTag = itemStack.getItemTag()
        if (itemTag.containsKey(NBT.MAX_DURABILITY.key)) {
            val maxDurability = itemTag[NBT.MAX_DURABILITY.key]!!.asInt()
            val min = min(max(value, 0), maxDurability)
            itemTag[NBT.DURABILITY.key] = ItemTagData(min)
            itemTag.saveTo(itemStack)
            itemStack.updateDurability(maxDurability, min)
        }
    }

    fun ItemStack.updateDurability() {
        val itemTag = getItemTag()
        if (itemTag.containsKey(NBT.MAX_DURABILITY.key)) {
            val maxDurability = itemTag[NBT.MAX_DURABILITY.key]!!.asInt()
            val durability = (itemTag[NBT.DURABILITY.key]?.asInt() ?: maxDurability)
            updateDurability(maxDurability, durability)
        }
    }

    private fun ItemStack.updateDurability(max: Int, current: Int) {
        // 当前耐久 / 最大耐久 得到耐久百分比
        val percent = Coerce.toDouble(current) / Coerce.toDouble(max)
        val durability = type.maxDurability
        // 原版物品最大耐久 - 最大耐久*百分比
        this.durability = Coerce.toShort(durability - Coerce.toDouble(durability) * percent)
    }


}
